From c6cd5e9c10edc68caf6936a3d3274f758e9cd03d Mon Sep 17 00:00:00 2001
From: Zdenek Dohnal <zdohnal@redhat.com>
Date: Tue, 3 Oct 2023 13:59:40 +0200
Subject: [PATCH] cups/hash.c: Put support for MacOS/Win SSL libs back

- I mustn't remove their support in patch release - this should happen in
2.5 only.
- I have put back support for several hashes as well - they
should be removed in 2.5.
- restrict usage of second block hashing only if OpenSSL/LibreSSL/GnuTLS
  is available

Upstream: https://github.com/OpenPrinting/cups/commit/c6cd5e9c10edc68caf6936a3d3274f758e9cd03d
Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
---
 cups/hash.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 260 insertions(+), 11 deletions(-)

diff --git a/cups/hash.c b/cups/hash.c
index 93ca552c8..c447bab4e 100644
--- a/cups/hash.c
+++ b/cups/hash.c
@@ -12,8 +12,13 @@
 #include "md5-internal.h"
 #ifdef HAVE_OPENSSL
 #  include <openssl/evp.h>
-#else // HAVE_GNUTLS
+#elif defined(HAVE_GNUTLS)
 #  include <gnutls/crypto.h>
+#elif __APPLE__
+#  include <CommonCrypto/CommonDigest.h>
+#elif _WIN32
+#  include <windows.h>
+#  include <bcrypt.h>
 #endif // HAVE_OPENSSL
 
 
@@ -193,17 +198,18 @@ hash_data(const char    *algorithm,	// I - Algorithm
           const void    *b,		// I - Second block or `NULL` for none
           size_t        blen)		// I - Length of second block or `0` for none
 {
+#if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
   unsigned	hashlen;		// Length of hash
   unsigned char	hashtemp[64];		// Temporary hash buffer
-#ifdef HAVE_OPENSSL
-  const EVP_MD	*md = NULL;		// Message digest implementation
-  EVP_MD_CTX	*ctx;			// Context
-#else // HAVE_GNUTLS
-  gnutls_digest_algorithm_t alg = GNUTLS_DIG_UNKNOWN;
-					// Algorithm
-  gnutls_hash_hd_t ctx;			// Context
-#endif // HAVE_OPENSSL
+#else
+  if (strcmp(algorithm, "md5") && (b || blen != 0))
+  {
+    // Second block hashing is not supported without OpenSSL or GnuTLS
+    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unsupported without GnuTLS or OpenSSL/LibreSSL."), 1);
 
+    return (-1);
+  }
+#endif
 
   if (!strcmp(algorithm, "md5"))
   {
@@ -223,6 +229,10 @@ hash_data(const char    *algorithm,	// I - Algorithm
   }
 
 #ifdef HAVE_OPENSSL
+  const EVP_MD	*md = NULL;		// Message digest implementation
+  EVP_MD_CTX	*ctx;			// Context
+
+
   if (!strcmp(algorithm, "sha"))
   {
     // SHA-1
@@ -244,6 +254,14 @@ hash_data(const char    *algorithm,	// I - Algorithm
   {
     md = EVP_sha512();
   }
+  else if (!strcmp(algorithm, "sha2-512_224"))
+  {
+    md = EVP_sha512_224();
+  }
+  else if (!strcmp(algorithm, "sha2-512_256"))
+  {
+    md = EVP_sha512_256();
+  }
 
   if (md)
   {
@@ -262,7 +280,13 @@ hash_data(const char    *algorithm,	// I - Algorithm
     return ((ssize_t)hashlen);
   }
 
-#else // HAVE_GNUTLS
+#elif defined(HAVE_GNUTLS)
+  gnutls_digest_algorithm_t	alg = GNUTLS_DIG_UNKNOWN;	// Algorithm
+  gnutls_hash_hd_t		ctx;				// Context
+  unsigned char		temp[64];			// Temporary hash buffer
+  size_t			tempsize = 0;			// Truncate to this size?
+
+
   if (!strcmp(algorithm, "sha"))
   {
     // SHA-1
@@ -284,9 +308,32 @@ hash_data(const char    *algorithm,	// I - Algorithm
   {
     alg = GNUTLS_DIG_SHA512;
   }
+  else if (!strcmp(algorithm, "sha2-512_224"))
+  {
+    alg      = GNUTLS_DIG_SHA512;
+    tempsize = 28;
+  }
+  else if (!strcmp(algorithm, "sha2-512_256"))
+  {
+    alg      = GNUTLS_DIG_SHA512;
+    tempsize = 32;
+  }
 
   if (alg != GNUTLS_DIG_UNKNOWN)
   {
+    if (tempsize > 0)
+    {
+      // Truncate result to tempsize bytes...
+
+      if (hashsize < tempsize)
+        goto too_small;
+
+      gnutls_hash_fast(alg, a, alen, temp);
+      memcpy(hash, temp, tempsize);
+
+      return ((ssize_t)tempsize);
+    }
+
     hashlen = gnutls_hash_get_len(alg);
 
     if (hashlen > hashsize)
@@ -302,7 +349,209 @@ hash_data(const char    *algorithm,	// I - Algorithm
 
     return ((ssize_t)hashlen);
   }
-#endif // HAVE_OPENSSL
+
+#elif __APPLE__
+  if (!strcmp(algorithm, "sha"))
+  {
+    // SHA-1...
+
+    CC_SHA1_CTX	ctx;			// SHA-1 context
+
+    if (hashsize < CC_SHA1_DIGEST_LENGTH)
+      goto too_small;
+
+    CC_SHA1_Init(&ctx);
+    CC_SHA1_Update(&ctx, a, (CC_LONG)alen);
+    CC_SHA1_Final(hash, &ctx);
+
+    return (CC_SHA1_DIGEST_LENGTH);
+  }
+#  ifdef CC_SHA224_DIGEST_LENGTH
+  else if (!strcmp(algorithm, "sha2-224"))
+  {
+    CC_SHA256_CTX	ctx;		// SHA-224 context
+
+    if (hashsize < CC_SHA224_DIGEST_LENGTH)
+      goto too_small;
+
+    CC_SHA224_Init(&ctx);
+    CC_SHA224_Update(&ctx, a, (CC_LONG)alen);
+    CC_SHA224_Final(hash, &ctx);
+
+    return (CC_SHA224_DIGEST_LENGTH);
+  }
+#  endif /* CC_SHA224_DIGEST_LENGTH */
+  else if (!strcmp(algorithm, "sha2-256"))
+  {
+    CC_SHA256_CTX	ctx;		// SHA-256 context
+
+    if (hashsize < CC_SHA256_DIGEST_LENGTH)
+      goto too_small;
+
+    CC_SHA256_Init(&ctx);
+    CC_SHA256_Update(&ctx, a, (CC_LONG)alen);
+    CC_SHA256_Final(hash, &ctx);
+
+    return (CC_SHA256_DIGEST_LENGTH);
+  }
+  else if (!strcmp(algorithm, "sha2-384"))
+  {
+    CC_SHA512_CTX	ctx;		// SHA-384 context
+
+    if (hashsize < CC_SHA384_DIGEST_LENGTH)
+      goto too_small;
+
+    CC_SHA384_Init(&ctx);
+    CC_SHA384_Update(&ctx, a, (CC_LONG)alen);
+    CC_SHA384_Final(hash, &ctx);
+
+    return (CC_SHA384_DIGEST_LENGTH);
+  }
+  else if (!strcmp(algorithm, "sha2-512"))
+  {
+    CC_SHA512_CTX	ctx;		// SHA-512 context
+
+    if (hashsize < CC_SHA512_DIGEST_LENGTH)
+      goto too_small;
+
+    CC_SHA512_Init(&ctx);
+    CC_SHA512_Update(&ctx, a, (CC_LONG)alen);
+    CC_SHA512_Final(hash, &ctx);
+
+    return (CC_SHA512_DIGEST_LENGTH);
+  }
+#  ifdef CC_SHA224_DIGEST_LENGTH
+  else if (!strcmp(algorithm, "sha2-512_224"))
+  {
+    CC_SHA512_CTX	ctx;		// SHA-512 context
+    unsigned char	temp[CC_SHA512_DIGEST_LENGTH];
+                                        // SHA-512 hash
+
+    // SHA2-512 truncated to 224 bits (28 bytes)...
+
+    if (hashsize < CC_SHA224_DIGEST_LENGTH)
+      goto too_small;
+
+    CC_SHA512_Init(&ctx);
+    CC_SHA512_Update(&ctx, a, (CC_LONG)alen);
+    CC_SHA512_Final(temp, &ctx);
+
+    memcpy(hash, temp, CC_SHA224_DIGEST_LENGTH);
+
+    return (CC_SHA224_DIGEST_LENGTH);
+  }
+#  endif // CC_SHA224_DIGEST_LENGTH
+  else if (!strcmp(algorithm, "sha2-512_256"))
+  {
+    CC_SHA512_CTX	ctx;		// SHA-512 context
+    unsigned char	temp[CC_SHA512_DIGEST_LENGTH];
+                                        // SHA-512 hash
+
+    // SHA2-512 truncated to 256 bits (32 bytes)...
+
+    if (hashsize < CC_SHA256_DIGEST_LENGTH)
+      goto too_small;
+
+    CC_SHA512_Init(&ctx);
+    CC_SHA512_Update(&ctx, a, (CC_LONG)alen);
+    CC_SHA512_Final(temp, &ctx);
+
+    memcpy(hash, temp, CC_SHA256_DIGEST_LENGTH);
+
+    return (CC_SHA256_DIGEST_LENGTH);
+  }
+
+#elif _WIN32
+  // Use Windows CNG APIs to perform hashing...
+  BCRYPT_ALG_HANDLE	alg;		// Algorithm handle
+  LPCWSTR		algid = NULL;	// Algorithm ID
+  ssize_t		hashlen;	// Hash length
+  NTSTATUS		status;		// Status of hash
+  unsigned char		temp[64];	// Temporary hash buffer
+  size_t		tempsize = 0;	// Truncate to this size?
+
+
+  if (!strcmp(algorithm, "sha"))
+  {
+    algid   = BCRYPT_SHA1_ALGORITHM;
+    hashlen = 20;
+  }
+  else if (!strcmp(algorithm, "sha2-256"))
+  {
+    algid   = BCRYPT_SHA256_ALGORITHM;
+    hashlen = 32;
+  }
+  else if (!strcmp(algorithm, "sha2-384"))
+  {
+    algid   = BCRYPT_SHA384_ALGORITHM;
+    hashlen = 48;
+  }
+  else if (!strcmp(algorithm, "sha2-512"))
+  {
+    algid   = BCRYPT_SHA512_ALGORITHM;
+    hashlen = 64;
+  }
+  else if (!strcmp(algorithm, "sha2-512_224"))
+  {
+    algid   = BCRYPT_SHA512_ALGORITHM;
+    hashlen = tempsize = 28;
+  }
+  else if (!strcmp(algorithm, "sha2-512_256"))
+  {
+    algid   = BCRYPT_SHA512_ALGORITHM;
+    hashlen = tempsize = 32;
+  }
+
+  if (algid)
+  {
+    if (hashsize < (size_t)hashlen)
+      goto too_small;
+
+    if ((status = BCryptOpenAlgorithmProvider(&alg, algid, NULL, 0)) < 0)
+    {
+      DEBUG_printf(("2cupsHashData: BCryptOpenAlgorithmProvider returned %d.", status));
+
+      if (status == STATUS_INVALID_PARAMETER)
+	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad algorithm parameter."), 1);
+      else
+	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to access cryptographic provider."), 1);
+
+      return (-1);
+    }
+
+    if (tempsize > 0)
+    {
+      // Do a truncated SHA2-512 hash...
+      status = BCryptHash(alg, NULL, 0, (PUCHAR)a, (ULONG)alen, temp, sizeof(temp));
+      memcpy(hash, temp, hashlen);
+    }
+    else
+    {
+      // Hash directly to buffer...
+      status = BCryptHash(alg, NULL, 0, (PUCHAR)a, (ULONG)alen, hash, (ULONG)hashlen);
+    }
+
+    BCryptCloseAlgorithmProvider(alg, 0);
+
+    if (status < 0)
+    {
+      DEBUG_printf(("2cupsHashData: BCryptHash returned %d.", status));
+
+      if (status == STATUS_INVALID_PARAMETER)
+	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad hashing parameter."), 1);
+      else
+	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Hashing failed."), 1);
+
+      return (-1);
+    }
+
+    return (hashlen);
+  }
+
+#else
+  if (hashsize < 64)
+    goto too_small;
+#endif // __APPLE__
 
   // Unknown hash algorithm...
   _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unknown hash algorithm."), 1);
